using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
using System;
using Random = UnityEngine.Random;

namespace CGF
{
    public class PrizeRoulette : MonoBehaviour
    {
        [Header("转轮")]
        public RectTransform wheel;
        [Header("转轮金属圈")]
        public RectTransform quan;
        [Header("小球")]
        public RectTransform ball;
        [Header("撞钉")]
        public RectTransform[] colliders;
        [Header("小球转圈半径")]
        public float ballRunRadius = 415;
        [Header("小球停止半径")]
        public float ballEndRadius = 238;
        [Header("小球转圈音效")]
        public AudioClip ballSound;
        [Header("小球撞击音效")]
        public AudioClip hitSound;

        public bool isRunning { get { return currBallState != BallState.Idle; } }
        public enum BallState { Idle, RotateForceful, RotateSlowDown, Bounce }
        public RectTransform rectTransform { get { return transform as RectTransform; } }

        private BallState currBallState = BallState.Idle;
        private bool hasCollider;
        private float currBallRadian, currBallRadius, bounceSpeed, offset;
        private int stopNumber;
        private Vector2 ballEndPos, guidePos, preBouncePos;
        private Tweener ballRotateTweener, ballRadiusTweener, wheelTweener;

        public void OnDestroy()
        {
            ball.DOKill();
            if (ballRotateTweener != null)
            {
                ballRotateTweener.Kill();
            }
            if (ballRadiusTweener != null)
            {
                ballRadiusTweener.Kill();
            }
            if (wheelTweener != null)
            {
                wheelTweener.Kill();
            }
        }

        void Awake()
        {
            Transform wheelTrans = this.rectTransform.Find("Wheel");
            if (!wheelTrans)
            {
                Debug.LogError("Roulette结构有误，子集找不到Wheel");
                return;
            }

            for (int i = 0; i < colliders.Length; i++)
            {
                Image image = colliders[i].GetComponent<Image>();
                image.enabled = false;
            }

            ball.gameObject.SetActive(false);
        }

        public void Update()
        {
            if (currBallState == BallState.RotateSlowDown && !hasCollider)
            {
                //碰撞检测8个撞钉
                for (int i = 0; i < colliders.Length; i++)
                {
                    RectTransform collider = colliders[i];
                    float dis = Vector2.Distance(ball.anchoredPosition, collider.anchoredPosition);
                    if (dis <= 25 && currBallState != BallState.Bounce)
                    {
                        Bounce(1);
                        hasCollider = true;
                    }
                }
            }
        }

        /// <summary>
        /// 隐藏或显示白球
        /// </summary>
        public void SetBallActive(bool b)
        {
            ball.gameObject.SetActive(b);
        }

        /// <summary>
        /// 开始旋转
        /// </summary>
        /// <param name="stopNumber">希望定制的数字（0-36）</param>
        /// <param name="onStop">结束回调</param>
        public async void StartRun(int stopNumber, Action onStop)
        {
            if (currBallState != BallState.Idle)
            {
                return;
            }
            this.stopNumber = stopNumber;
            currBallState = BallState.RotateForceful;

            float wheelStopAngle = Random.Range(0, 360);
            float ballStopAngle = GetNumAngle(stopNumber, false, wheelStopAngle);

            //内轮
            float wheelDuration = 15;
            int wheelRoundCount = 4;
            wheelTweener = DOVirtual.Float(wheel.localEulerAngles.z, wheel.localEulerAngles.z + 130, 1, (float v) =>
            {
                if (!gameObject) return;
                wheel.localEulerAngles = new Vector3(0, 0, v);
                quan.eulerAngles = Vector3.zero;
            }).SetEase(Ease.InSine).OnComplete(() =>
            {
                if (!gameObject) return;
                float wheelBeginRadian = wheelStopAngle - 360 * wheelRoundCount - (wheelStopAngle - wheel.localEulerAngles.z);
                wheelTweener = DOVirtual.Float(wheelBeginRadian, wheelStopAngle, wheelDuration, (float v) =>
                {
                    if (!gameObject) return;
                    wheel.localEulerAngles = new Vector3(0, 0, v);
                    quan.eulerAngles = Vector3.zero;
                }).OnComplete(() =>
                {
                    if (!gameObject) return;
                    currBallState = BallState.Idle;
                    onStop?.Invoke();
                });
            });

            //白球等待轮盘先转的时间
            float ballWait = 1f;
            await CTask.Wait(ballWait);

            //白球由大变小模拟丢下
            SoundPlayer.Play(ballSound);
            ball.gameObject.SetActive(true);
            float duration = 0.1f;
            DOVirtual.Float(ballRunRadius * 0.5f, ballRunRadius, duration, (float v) =>
            {
                if (!gameObject) return;
                currBallRadius = v;
            });
            //白球真正逻辑，以上模拟丢下可完全删除
            int ballStopOffsetAngle = 70; //白球需要提前进入弹跳状态，所以停止的目标角度要减去几度
            float ballDuration = 12;
            int ballRoundCount = 9;
            float ballBegin = (ballStopAngle + 360 * ballRoundCount) * Mathf.Deg2Rad;
            float ballOutsideEndRadian = (ballStopAngle - ballStopOffsetAngle) * Mathf.Deg2Rad;

            ball.SetParent(rectTransform, true);
            ball.SetSiblingIndex(5);
            ballRotateTweener = DOVirtual.Float(ballBegin, ballOutsideEndRadian, ballDuration - ballWait, (float v) =>
            {
                if (!gameObject) return;
                if (currBallState == BallState.RotateForceful || currBallState == BallState.RotateSlowDown)
                {
                    currBallRadian = v;
                    Vector2 pos;
                    pos.x = Mathf.Cos(currBallRadian) * currBallRadius;
                    pos.y = Mathf.Sin(currBallRadian) * currBallRadius;
                    ball.anchoredPosition = pos;
                }
            });

            //白球半径
            float radiusDuration = 1.5f;
            ballRadiusTweener = DOVirtual.Float(ballRunRadius, ballRunRadius * 0.55f, radiusDuration, (float v) =>
            {
                if (!gameObject) return;
                if (currBallState == BallState.RotateForceful || currBallState == BallState.RotateSlowDown)
                {
                    if (currBallState == BallState.RotateForceful)
                    {
                        hasCollider = false;
                        currBallState = BallState.RotateSlowDown;
                    }
                    currBallRadius = v;

                    //半径缩小到转盘时开始弹跳
                    if (currBallRadius < 327 && currBallState != BallState.Bounce)
                    {
                        Bounce(1);
                    }
                }
            }).SetEase(Ease.InSine).SetDelay(ballDuration - ballWait - radiusDuration);
        }

        private void Bounce(int times)
        {
            if (times < Random.Range(2, 6))
            {
                SoundPlayer.Play(hitSound);
            }

            if (times == 1)
            {
                currBallState = BallState.Bounce;

                float ballInsideEndRadian = Radian360(GetNumAngle(stopNumber, true, 0) * Mathf.Deg2Rad);
                ballEndPos.x = Mathf.Cos(ballInsideEndRadian) * ballEndRadius;
                ballEndPos.y = Mathf.Sin(ballInsideEndRadian) * ballEndRadius;

                currBallRadian -= Random.Range(0f, Mathf.PI * 0.1f);
                currBallRadius = Random.Range(230f, 300f);
                preBouncePos.x = Mathf.Cos(currBallRadian) * currBallRadius;
                preBouncePos.y = Mathf.Sin(currBallRadian) * currBallRadius;
                float dis = Vector2.Distance(ball.anchoredPosition, preBouncePos);
                float duration = dis / 350;
                ball.DOAnchorPos(preBouncePos, duration).SetEase(Ease.Linear).OnComplete(() =>
                {
                    if (!gameObject) return;
                    Bounce(++times);
                });
            }
            else
            {
                if (times == 2)
                {
                    ball.SetParent(wheel, true);
                    guidePos = ball.anchoredPosition;
                    offset = 60;
                    bounceSpeed = 600;
                }

                guidePos += (ballEndPos - guidePos) * 0.5f;

                float radius = 0;
                if (Vector2.Distance(preBouncePos, Vector2.zero) < ballEndRadius)
                {
                    //前一个弹在内部，下一个要在外部
                    radius = ballEndRadius + Random.Range(0, offset);
                }
                else
                {
                    //前一个弹在外部，下一个要在内部
                    radius = ballEndRadius - Random.Range(0, offset);
                }
                Vector2 nextPos;
                float radian = Mathf.Atan2(guidePos.y, guidePos.x);
                nextPos.x = Mathf.Cos(radian) * radius;
                nextPos.y = Mathf.Sin(radian) * radius;
                preBouncePos = nextPos;

                offset *= 0.8f;

                if (Vector2.Distance(ball.anchoredPosition, ballEndPos) > 2)
                {
                    float dis = Vector2.Distance(ball.anchoredPosition, nextPos);
                    float duration = dis / bounceSpeed;
                    bounceSpeed *= 0.8f;
                    if (bounceSpeed < 50) bounceSpeed = 50;
                    ball.DOAnchorPos(nextPos, duration).SetEase(Ease.Linear).OnComplete(() =>
                    {
                        if (!gameObject) return;
                        Bounce(++times);
                    });
                }
            }
        }

        private float Radian360(float radian)
        {
            while (radian < 0) radian += Mathf.PI * 2f;
            while (radian > Mathf.PI * 2f) radian -= Mathf.PI * 2f;
            return radian;
        }

        private float IncludeRadian(float radianA, float radianB)
        {
            float sub = Mathf.Abs(radianA - radianB);
            if (sub > 180)
            {
                return 360 - sub;
            }
            return sub;
        }

        private float GetNumAngle(int num, bool isGetInsidePos, float wheelAngle)
        {
            float angleOffset = 2;
            int[] nums = { 12, 35, 3, 26, 0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30, 8, 23, 10, 5, 24, 16, 33, 1, 20, 14, 31, 9, 22, 18, 29, 7, 28 };
            int numIndex = 0;
            for (int i = 0; i < nums.Length; i++)
            {
                if (nums[i] == num)
                {
                    numIndex = i;
                    break;
                }
            }
            float subAngle = 360 / (float)nums.Length;
            if (isGetInsidePos)
            {
                return -subAngle * numIndex - angleOffset;
            }
            else
            {
                return wheelAngle - subAngle * numIndex - angleOffset;
            }
        }
    }
}
